/*
 * $Id: TcpServer.cc,v 1.14 2006-05-11 11:54:27 bacon Exp $
 */

#include <xpp/net/TcpServer.h>
#include <xp/bas/time.h>
		
namespace xpp
{
	namespace net
	{

		using namespace xpp::bas;

		TcpServer::Client::Client (TcpServer* server) 
		{
			this->server = server;
		}

		//
		// NOTICE: the guarantee class below could have been placed 
		//         inside TCPServer::Client::run () without supporting 
		//         old C++ compilers.
		//
		class guarantee_tcpsocket_close {
		public:
			guarantee_tcpsocket_close (TcpSocket* socket): psck (socket) {}
			~guarantee_tcpsocket_close () { psck->shutdown (); psck->close (); }
			TcpSocket* psck;
		};
		
		int TcpServer::Client::run ()
		{
			// blockAllSignals is called inside run because 
			// Client is instantiated in the TcpServer thread.
			// so if it is called in the constructor of Client, 
			// it would just block signals to the TcpProxy thread.
			blockAllSignals (); // don't care about the result.

			guarantee_tcpsocket_close close_socket (&socket);
			if (server->handle_client (&socket, &address) == -1) return -1;
			return 0;
		}

		int TcpServer::Client::stop ()
		{
			// the receiver will be notified of the end of 
			// the connection by the socket's closing.
			// therefore, handle_client() must return
			// when it detects the end of the connection.
			//
			// TODO: must think of a better way to do this 
			//       as it might not be thread-safe.
			//       but it is still ok because Client::stop() 
			//       is rarely called.
			socket.shutdown ();
			socket.close ();
			return 0;
		}

		TcpServer::TcpServer ()
		{
			server_serving  = false;
			stop_requested  = false;
			max_connections = 0;
		}

		TcpServer::TcpServer (
			const TcpAddress& address): binding_address (address)
		{
			server_serving  = false;
			stop_requested  = false;
			max_connections = 0;
		}

		TcpServer::~TcpServer ()
		{
			// xp_assert (server_serving == false);
			delete_all_clients ();
		}
		
		int TcpServer::start (int* err_code)
		{
			server_serving = true;
			if (err_code != XP_NULL) *err_code = ERR_NONE;
		
			setStopRequested (false);

			Client* client = XP_NULL;

			try {
				TcpSocket socket;
		
				if (socket.open (binding_address) == -1) {
					if (err_code) *err_code = ERR_OPEN;
					server_serving = false;
					setStopRequested (false);
					return -1;
				}

				socket.setReuseAddr (1);
				if (socket.bind () == -1) {
					if (err_code) *err_code = ERR_BIND;
					server_serving = false;
					setStopRequested (false);
					return -1;
				}
		
				if (socket.listen () <= -1) {
					if (err_code) *err_code = ERR_LISTEN;
					server_serving = false;
					setStopRequested (false);
					return -1;
				}
		
				socket.enableTimeout (1000);

				while (!isStopRequested()) 
				{
					delete_dead_clients ();
		
					if (max_connections > 0 &&
					    max_connections <= client_list.size()) {
						socket.drop ();
						continue;
					}

					if (client == XP_NULL)
					{
						try { client = new Client (this); } 
						catch (...) { }
					}
					if (client == XP_NULL) {
						// memory alloc failed
						socket.drop ();
						continue;
					}

					if (socket.accept(&client->socket, 
						&client->address) == XP_NULL) 
					{
						// can't do much if accept fails
						continue;
					}

					#ifdef _WIN32
					if (client->start(Thread::DETACHED) == -1) 
					#else
					if (client->start(0) == -1)
					#endif
					{
						delete client; 
						client = XP_NULL;
						continue;
					}

					client_list.append (client);
					client = XP_NULL;
				}

				delete_all_clients ();
				if (client != XP_NULL) delete client;
			}
			catch (...) 
			{
				delete_all_clients ();
				if (client != XP_NULL) delete client;
		
				if (err_code) *err_code = ERR_EXCEPTION;
				server_serving = false;
				setStopRequested (false);
		
				return -1;
			}
		
			server_serving = false;
			setStopRequested (false);
			return 0;
		}
		
		int TcpServer::stop ()
		{
			if (server_serving) setStopRequested (true);
			return 0;
		}

		void TcpServer::delete_dead_clients ()
		{
			ClientList::Node* np, * np2;
			
			np = client_list.head();
			while (np) 
			{
				Client* p = np->value;
				xp_assert (p != XP_NULL);
		
				if (p->state() != Thread::RUNNING) {
		#ifndef _WIN32
					p->join ();
		#endif
					delete p;
					np2 = np; np = np->forward();
					client_list.remove (np2);
					continue;
				}
		
				np = np->forward();
			}
		}
		
		void TcpServer::delete_all_clients ()
		{
			ClientList::Node* np, * np2;
			Client* p;
		
			for (np = client_list.head(); np; np = np->forward()) 
			{
				p = np->value;
				if (p->state() == Thread::RUNNING) p->stop();
			}
		
			np = client_list.head();
			while (np != XP_NULL) 
			{
				p = np->value;
				xp_assert (p != XP_NULL);
		
		#ifdef _WIN32
				while (p->state() == Thread::RUNNING) xp_sleep (300);
		#else	
				p->join ();
		#endif
				delete p;
				np2 = np; np = np->forward();
				client_list.remove (np2);	
			}
		}
		
	}
}
